<?php

namespace common\core;

use app\models\RoleTask;
use app\models\Task;
use app\models\TaskItem;
use app\models\UserRole;
use yii\web\ConflictHttpException;

/**
 * 自己重写验证管理器,继承了接口的方法，但是因为表变化了很多都需要重写
 * User: zhangchao
 * Date: 15/6/1
 * Time: 15:46
 */
//implements \yii\rbac\ManagerInterface  先不继承接口，自己重写需要的方法
class ZAuthManager extends \yii\base\Component
{

//    public $db = 'db';
    public $useRbac = true;
    const TASK_ITEM_KEY = 'task_item';
    const ROLE_TASK_KEY = 'role_task';
    const ROLE_ITEM_KEY = 'role_item';



    /**
     * Checks if the user has the specified permission.
     * @param string|integer $userId the user ID. This should be either an integer or a string representing
     * the unique identifier of a user. See [[\yii\web\User::id]].
     * @param string $permissionName the name of the permission to be checked against
     * @param array $params name-value pairs that will be passed to the rules associated
     * with the roles and permissions assigned to the user.
     * @return boolean whether the user has the specified permission.
     * @throws \yii\base\InvalidParamException if $permissionName does not refer to an existing permission
     */
    public function checkAccess($userId,$permissionName,$params = [])
    {

        //如果不启用rbac，那就无需验证
        if(!\Yii::$app->authManager->useRbac){
            return true;
        }
        //先查出用户的角色 @todo 改成缓存
        $user_roles = UserRole::find()->where(['user_id' => $userId])->asArray()->select('role_id')->all();
        if(empty($user_roles)){
            throw new ConflictHttpException('此用户还没有角色，请联系管理员！');
        }

        //获取所有角色对应的权限
        $taskItems = \Yii::$app->authManager->getAllRoleItem(false);
        if(empty($taskItems)){
            throw new ConflictHttpException('获取角色授权失败，请联系管理员！');
        }


        $user_permissions = [];
        foreach($user_roles as $role){
            if(isset($taskItems[$role['role_id']])){
                $user_permissions = array_merge($user_permissions,$taskItems[$role['role_id']]);
            }
        }

        if(!in_array($permissionName , $user_permissions)){
            throw new ConflictHttpException('未获得此权限的授权！action：'.$permissionName);
            return false;
        }

        return  true;


    }


    /**
     * 获取角色和授权项的关系
     * @param bool $useCache 是否使用缓存
     * @return array
     */
    public function getAllRoleItem($useCache = true)
    {
        //先从缓存中取得
        if($useCache){
            $roleItems = \Yii::$app->cache->get(self::ROLE_ITEM_KEY);
            if($roleItems){
                return $roleItems;
            }
        }
        $taskItems = $this->getAllTaskItem($useCache);
        $roleTasks = $this->getAllRoleTask($useCache);
        if(empty($taskItems) || empty($roleTasks)){
            return [];
        }



        $result = [];
        foreach($roleTasks as $key => $value){
            $data = [];
            foreach($value as $v){
                if(isset($taskItems[$v])){
                    $data = array_merge($data,$taskItems[$v]) ;
                }
            }

            $result[$key] = $data;
        }

        \Yii::$app->cache->set(self::ROLE_ITEM_KEY,$result);
        return $result;

    }


    /**
     * 获取角色和任务的对应关系
     * @param bool $useCache 是否使用缓存
     * @return array
     *
     */
    public function getAllRoleTask($useCache = true)
    {

        if($useCache){
            $roleTasks = \Yii::$app->cache->get(self::ROLE_TASK_KEY);
            if($roleTasks){
                return $roleTasks;
            }
        }
        //查询出结果集
        $roleTasks = RoleTask::find()->with('task')->asArray()->all();
        if (empty($roleTasks)) {
            return [];
        }
        //组合出数据
        $data = [];
        foreach ($roleTasks as $value) {
            $data[$value['role_id']][] = $value['task_id'];
        }

        \Yii::$app->cache->set(self::ROLE_TASK_KEY,$data);
        return $data;
    }

    /**
     * 获取任务和授权项的关系
     * @param bool $useCache 是否使用缓存数据
     * @return array
     */
    public function getAllTaskItem($useCache = true)
    {
        //如果使用缓存，先从缓存里面取得数据
        if($useCache){
            $taskItems = \Yii::$app->cache->get(self::TASK_ITEM_KEY);
            if($taskItems){
                return $taskItems;
            }
        }
        //缓存不存在时，查询数据库
        $taskItems = TaskItem::find()->asArray()->all();
        if (empty($taskItems)) {
            return [];
        }

        //构建结果街 key => task_id , value => [授权项数组]
        $data = [];
        foreach ($taskItems as $value) {

            $data[$value['task_id']][] = $value['auth_item'];
        }

        \Yii::$app->cache->set(self::TASK_ITEM_KEY,$data);
        return $data;

    }

    /**
     * Creates a new Role object.
     * Note that the newly created role is not added to the RBAC system yet.
     * You must fill in the needed data and call [[add()]] to add it to the system.
     * @param string $name the role name
     * @return \yii\rbac\Role the new Role object
     */
    public function createRole($name)
    {
        // TODO: Implement createRole() method.
    }

    /**
     * Creates a new Permission object.
     * Note that the newly created permission is not added to the RBAC system yet.
     * You must fill in the needed data and call [[add()]] to add it to the system.
     * @param string $name the permission name
     * @return \yii\rbac\Permission the new Permission object
     */
    public function createPermission($name)
    {
        // TODO: Implement createPermission() method.
    }

    /**
     * Adds a role, permission or rule to the RBAC system.
     * @param \yii\rbac\Role|\yii\rbac\Permission|\yii\rbac\Rule $object
     * @return boolean whether the role, permission or rule is successfully added to the system
     * @throws \Exception if data validation or saving fails (such as the name of the role or permission is not unique)
     */
    public function add($object)
    {
        // TODO: Implement add() method.
    }

    /**
     * Removes a role, permission or rule from the RBAC system.
     * @param \yii\rbac\Role|\yii\rbac\Permission|\yii\rbac\Rule $object
     * @return boolean whether the role, permission or rule is successfully removed
     */
    public function remove($object)
    {
        // TODO: Implement remove() method.
    }

    /**
     * Updates the specified role, permission or rule in the system.
     * @param string $name the old name of the role, permission or rule
     * @param \yii\rbac\Role|\yii\rbac\Permission|\yii\rbac\Rule $object
     * @return boolean whether the update is successful
     * @throws \Exception if data validation or saving fails (such as the name of the role or permission is not unique)
     */
    public function update($name, $object)
    {
        // TODO: Implement update() method.
    }

    /**
     * Returns the named role.
     * @param string $name the role name.
     * @return null|\yii\rbac\Role the role corresponding to the specified name. Null is returned if no such role.
     */
    public function getRole($name)
    {
        // TODO: Implement getRole() method.
    }

    /**
     * Returns all roles in the system.
     * @return \yii\rbac\Role[] all roles in the system. The array is indexed by the role names.
     */
    public function getRoles()
    {
        // TODO: Implement getRoles() method.
    }

    /**
     * Returns the roles that are assigned to the user via [[assign()]].
     * Note that child roles that are not assigned directly to the user will not be returned.
     * @param string|integer $userId the user ID (see [[\yii\web\User::id]])
     * @return \yii\rbac\Role[] all roles directly or indirectly assigned to the user. The array is indexed by the role names.
     */
    public function getRolesByUser($userId)
    {
        // TODO: Implement getRolesByUser() method.
    }

    /**
     * Returns the named permission.
     * @param string $name the permission name.
     * @return null|\yii\rbac\Permission the permission corresponding to the specified name. Null is returned if no such permission.
     */
    public function getPermission($name)
    {
        // TODO: Implement getPermission() method.
    }

    /**
     * Returns all permissions in the system.
     * @return \yii\rbac\Permission[] all permissions in the system. The array is indexed by the permission names.
     */
    public function getPermissions()
    {
        // TODO: Implement getPermissions() method.
    }

    /**
     * Returns all permissions that the specified role represents.
     * @param string $roleName the role name
     * @return \yii\rbac\Permission[] all permissions that the role represents. The array is indexed by the permission names.
     */
    public function getPermissionsByRole($roleName)
    {
        // TODO: Implement getPermissionsByRole() method.
    }

    /**
     * Returns all permissions that the user has.
     * @param string|integer $userId the user ID (see [[\yii\web\User::id]])
     * @return \yii\rbac\Permission[] all permissions that the user has. The array is indexed by the permission names.
     */
    public function getPermissionsByUser($userId)
    {
        // TODO: Implement getPermissionsByUser() method.
    }

    /**
     * Returns the rule of the specified name.
     * @param string $name the rule name
     * @return null|\yii\rbac\Rule the rule object, or null if the specified name does not correspond to a rule.
     */
    public function getRule($name)
    {
        // TODO: Implement getRule() method.
    }

    /**
     * Returns all rules available in the system.
     * @return \yii\rbac\Rule[] the rules indexed by the rule names
     */
    public function getRules()
    {
        // TODO: Implement getRules() method.
    }

    /**
     * Adds an item as a child of another item.
     * @param \yii\rbac\Item $parent
     * @param \yii\rbac\Item $child
     * @throws \yii\base\Exception if the parent-child relationship already exists or if a loop has been detected.
     */
    public function addChild($parent, $child)
    {
        // TODO: Implement addChild() method.
    }

    /**
     * Removes a child from its parent.
     * Note, the child item is not deleted. Only the parent-child relationship is removed.
     * @param \yii\rbac\Item $parent
     * @param \yii\rbac\Item $child
     * @return boolean whether the removal is successful
     */
    public function removeChild($parent, $child)
    {
        // TODO: Implement removeChild() method.
    }

    /**
     * Removed all children form their parent.
     * Note, the children items are not deleted. Only the parent-child relationships are removed.
     * @param \yii\rbac\Item $parent
     * @return boolean whether the removal is successful
     */
    public function removeChildren($parent)
    {
        // TODO: Implement removeChildren() method.
    }

    /**
     * Returns a value indicating whether the child already exists for the parent.
     * @param \yii\rbac\Item $parent
     * @param \yii\rbac\Item $child
     * @return boolean whether `$child` is already a child of `$parent`
     */
    public function hasChild($parent, $child)
    {
        // TODO: Implement hasChild() method.
    }

    /**
     * Returns the child permissions and/or roles.
     * @param string $name the parent name
     * @return \yii\rbac\Item[] the child permissions and/or roles
     */
    public function getChildren($name)
    {
        // TODO: Implement getChildren() method.
    }

    /**
     * Assigns a role to a user.
     *
     * @param \yii\rbac\Role $role
     * @param string|integer $userId the user ID (see [[\yii\web\User::id]])
     * @return \yii\rbac\Assignment the role assignment information.
     * @throws \Exception if the role has already been assigned to the user
     */
    public function assign($role, $userId)
    {
        // TODO: Implement assign() method.
    }

    /**
     * Revokes a role from a user.
     * @param \yii\rbac\Role $role
     * @param string|integer $userId the user ID (see [[\yii\web\User::id]])
     * @return boolean whether the revoking is successful
     */
    public function revoke($role, $userId)
    {
        // TODO: Implement revoke() method.
    }

    /**
     * Revokes all roles from a user.
     * @param mixed $userId the user ID (see [[\yii\web\User::id]])
     * @return boolean whether the revoking is successful
     */
    public function revokeAll($userId)
    {
        // TODO: Implement revokeAll() method.
    }

    /**
     * Returns the assignment information regarding a role and a user.
     * @param string|integer $userId the user ID (see [[\yii\web\User::id]])
     * @param string $roleName the role name
     * @return null|\yii\rbac\Assignment the assignment information. Null is returned if
     * the role is not assigned to the user.
     */
    public function getAssignment($roleName, $userId)
    {
        // TODO: Implement getAssignment() method.
    }

    /**
     * Returns all role assignment information for the specified user.
     * @param string|integer $userId the user ID (see [[\yii\web\User::id]])
     * @return \yii\rbac\Assignment[] the assignments indexed by role names. An empty array will be
     * returned if there is no role assigned to the user.
     */
    public function getAssignments($userId)
    {
        // TODO: Implement getAssignments() method.
    }

    /**
     * Removes all authorization data, including roles, permissions, rules, and assignments.
     */
    public function removeAll()
    {
        // TODO: Implement removeAll() method.
    }

    /**
     * Removes all permissions.
     * All parent child relations will be adjusted accordingly.
     */
    public function removeAllPermissions()
    {
        // TODO: Implement removeAllPermissions() method.
    }

    /**
     * Removes all roles.
     * All parent child relations will be adjusted accordingly.
     */
    public function removeAllRoles()
    {
        // TODO: Implement removeAllRoles() method.
    }

    /**
     * Removes all rules.
     * All roles and permissions which have rules will be adjusted accordingly.
     */
    public function removeAllRules()
    {
        // TODO: Implement removeAllRules() method.
    }

    /**
     * Removes all role assignments.
     */
    public function removeAllAssignments()
    {
        // TODO: Implement removeAllAssignments() method.
    }
}